package me.ycy.android.couchbase.server

import android.app.Notification
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.ServiceConnection
import android.content.BroadcastReceiver
import android.os.{ IBinder, Environment }
import android.util.Log

import com.couchbase.android.CouchbaseMobile
import com.couchbase.android.ICouchbaseDelegate
import com.couchbase.android.Intents.CouchbaseError
import com.couchbase.android.Intents.CouchbaseStarted

import scala.collection.JavaConverters._

import org.ektorp._
import org.ektorp.http.AndroidHttpClient
import org.ektorp.impl.{ StdCouchDbInstance, StdObjectMapperFactory,
                         StdCouchDbConnector }

import java.util.{ Timer, TimerTask, Date }

import replication._

object CouchbaseService {
  final val Tag: String = "CouchbaseServer"
  // NOTE: NotificationId should not be 0
  final val NotificationId: Int = 1

  final val ExtraForce = "Force"

  var port: Int = -1
  var host: String = "127.0.0.1"

  object ServiceState extends Enumeration {
    type ServiceState = Value
    val Stopped, Starting, Started = Value
  }
  import ServiceState._

  @volatile protected var state: ServiceState = Stopped
  val stateChangeLock = new Object

  var couch: CouchbaseMobile = null

  val binder: ICouchbaseService.Stub = new ICouchbaseService.Stub {
    def getPort = port
    def getHost = host
  }

  val timer = new java.util.Timer("Replication retry timer", true)
}

class CouchbaseService extends Service {
  import Context.NOTIFICATION_SERVICE

  private var startId: Int = -1
  private var connection: ServiceConnection = null

  var dbInstance: CouchDbInstance = null

  import CouchbaseService._
  import ServiceState._

  // NOTE: some problem for parse replication response
  // some filed are unknown and not mark ignorable
  class MyObjectMapperFactory extends StdObjectMapperFactory {
    import org.codehaus.jackson.map.DeserializationConfig

    val prop = DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES
    override def createObjectMapper() = {
      val om = super.createObjectMapper()
      om.configure(prop, false)
      om
    }

    override def createObjectMapper(c: CouchDbConnector) = {
      val om = super.createObjectMapper(c)
      om.configure(prop, false)
      om
    }
  }

  private val mReceiver = new BroadcastReceiver() {
    private def onStarted(host: String, port: Int): Unit = {
      Log.i(Tag, "Couchbase listen on: " + host + ":" + port);

      state = Started
      CouchbaseService.host = host
      CouchbaseService.port = port

      val httpClient = new AndroidHttpClient.Builder().
      host(host).port(port).build

      dbInstance = new StdCouchDbInstance(httpClient,
                                          new MyObjectMapperFactory)
      ReplicatorDb.db = new StdCouchDbConnector("_replicator", dbInstance)
      try {
        ReplicatorDb.db.createDatabaseIfNotExists()
      }
      catch {
        case e => Log.e(Tag, "create db failed.")
      }
      updateNotify("Started", host + ":" + port)
    }

    private def onError(error: String) = {
      Log.i(Tag, "Couchbase exit: " + error)
      state = Stopped
      dbInstance = null
      ReplicatorDb.db = null
      updateNotify("Exit", error)
    }

    override def onReceive(context: Context, intent: Intent): Unit = {
      if (CouchbaseStarted.ACTION.equals(intent.getAction())) {
        onStarted(CouchbaseStarted.getHost(intent),
                  CouchbaseStarted.getPort(intent))
      }
      else if(CouchbaseError.ACTION.equals(intent.getAction())) {
        onError(CouchbaseError.getMessage(intent))
      }
    }
  }

  def isMediaMounted(): Boolean = {
    // if meida not mounted, jut return
    val status = Environment.getExternalStorageState();

    //the SD Card is mounted as read-only, but we require it to be writable.
    if (status.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) {
      Log.i(Tag, "meida mounted read only")
      return false
    }

    //your handset is mounted as a USB device
    if (status.equals(Environment.MEDIA_SHARED)) {
      Log.i(Tag, "media mounted as usb device")
      return false;
    }

    //no SD Card inserted
    if (!status.equals(Environment.MEDIA_MOUNTED)) {
      Log.i(Tag, "media is not mounted")
      return false;
    }
    true
  }

  private def updateNotify(ticker: String, text: String) = {
    val n = new Notification(R.drawable.icon, ticker,
                             System.currentTimeMillis())
    val pi = PendingIntent.getActivity(this, 0,
                                       new Intent(this, classOf[MainActivity]),
                                       0)
    n.setLatestEventInfo(this, getText(R.string.app_name), text, pi)
    n.flags = Notification.FLAG_ONGOING_EVENT

    startForeground(NotificationId, n)
  }


  override def onCreate = {
    Log.d(Tag, "service onCreate")

    couch = new CouchbaseMobile(getApplicationContext)
    registerReceiver(mReceiver, new IntentFilter(CouchbaseStarted.ACTION))
    registerReceiver(mReceiver, new IntentFilter(CouchbaseError.ACTION))
    couch.startCouchbase()
    updateNotify("Starting", "")

    // start replication thread
    ReplicatorDb.running = true
    ReplicatorDb.start
  }

  override def onStartCommand(intent: Intent, flags: Int, startId: Int): Int = {
    Log.d(Tag, "service onStartCommand")
    couch.startCouchbase()
    return Service.START_STICKY
  }

  override def onDestroy: Unit = {
    Log.d(Tag, "service onDestroy")
    stopForeground(true)
    ReplicatorDb.running = false
    ReplicatorDb.interrupt
  }

  override def onLowMemory: Unit = {
    Log.d(Tag, "Low memory.")
  }

  def onBind(intent: Intent): IBinder = {
    Log.d(Tag, "service onBind")
    couch.startCouchbase()
    return binder
  }
}
